/**
 * 处理器   解析
 */
class Compile {
    constructor(el, vm) {
        //解析el
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        //赋值vue实例
        this.vm = vm;
        // 创建一个文档碎片  （虚拟DOM）
        const fragment = this.documentFragment(this.el);

        // 编译这个文档
        this.compileDocument(fragment);
        //将虚拟DOM移动到页面
        this.el.appendChild(fragment);
    }
    // 编译文档
    compileDocument(fragment) {
        //获取子节点
        let childNodes = fragment.childNodes;
        [...childNodes].map(item => {
            if (this.isElementNode(item)) {
                //元素类型,编译元素
                this.compileElement(item);
            } else {
                // 文本类型,编译文本
                this.compileText(item);
            }
            //判断元素类型下面的子节点
            if (item.childNodes && item.childNodes.length > 0) {
                this.compileDocument(item);
            }
        })
    }
    // 判断传入的值是DOM节点还是选择器
    isElementNode(el) {
        return el.nodeType === 1;
    }
    //编译元素类型
    compileElement(node) {
        //获取节点的所有属性
        const attributes = node.attributes;
        [...attributes].map(attr => {
            const {
                name,
                value
            } = attr;
            if (this.isDirective(name)) {
                //说明是vue指令
                const [, dirName] = name.split('-');
                //判断  v-on:click  v-bind:click
                const [dir, eventName] = dirName.split(':');
                //更新数据   数据驱动视图
                compileUtils[dir](node, value, this.vm, eventName);
                //删除页面vue指令
                // node.removeAttribute(dirName);
            } else if (this.isEventName(name)) {
                const [, eventName] = name.split('@');
                compileUtils['on'](node, value, this.vm, eventName);
            } else if (this.isAttrName(name)) {
                const [, eventName] = name.split(':');
                compileUtils['bind'](node, value, this.vm, eventName);
            }
        })
    }
    //判断事件
    isEventName(attrName) {
        return attrName.startsWith('@');
    }
    //判断属性
    isAttrName(attrName) {
        return attrName.startsWith(':');
    }
    //判断是否是vue指令
    isDirective(attrName) {
        return attrName.startsWith('v-');
    }
    //编译文本类型
    compileText(node) {
        //获取文本内容
        let text = node.textContent;
        //正则匹配{{}}里面的内容
        if (/\{\{(.+?)\}\}/.test(text)) {
            compileUtils['text'](node, text, this.vm);
        }
    }
    //创建文档碎片
    documentFragment(el) {
        // 创建虚拟DOM   方便后期的html渲染
        let fragment = document.createDocumentFragment();
        let childNode;
        //将页面上所有的节点移动到这个虚拟DOM中
        while (childNode = el.firstChild) {
            fragment.appendChild(childNode);
        }
        return fragment;
    }
}
// 编译工具类
const compileUtils = {
    //获取data里面对应的值
    getVal(value, vm) {
        return value.split('.').reduce((prev, current) => {
            return prev[current]
        }, vm.$data)
    },
    //设置双向绑定的值
    setVal(value,vm,newVal){
       value.split('.').reduce((prev, current) => {
            return typeof(prev[current])!=='object' ? prev[current] = newVal :prev[current]
        }, vm.$data)
    },
    //获取{{}}中的值
    getContetnVal(value,vm){
       return value.replace(/\{\{(.+?)\}\}/g, (...args) => {
            //args代表这个函数中所以的参数
            return this.getVal(args[1], vm);
        });
    },
    //更新text
    text(node, value, vm) {
        let content;
        //判断是否有{{}}
        if (value.indexOf('{{') !== -1) {
          
            /*replace第一个参数为正则且第二个参数为方法时  有四个参数 1.代表匹配的字符 2.代表正则括号里面的内容 
            3.正则匹配到的每段字符的第一个字符的索引 4.用于匹配的字符串主体 
            */
            content = value.replace(/\{\{(.+?)\}\}/g, (...args) => {
                new Watcher(args[1], vm, () => {
                node.textContent = this.getContetnVal(value,vm);
                })
                //args代表这个函数中所以的参数
                return this.getVal(args[1], vm);
            });
        } else {
            new Watcher(value, vm, () => {
                node.textContent = this.getVal(value, vm);
            })
            content = this.getVal(value, vm);
        }
        node.textContent = content;
    },
    //更新html
    html(node, value, vm) {
        //创建观察者
        new Watcher(value, vm, () => {
            node.innerHTML = this.getVal(value, vm);
        })
        node.innerHTML = this.getVal(value, vm);
    },
    //更新表单
    model(node, value, vm) {
        // 数据更新视图
        new Watcher(value, vm, () => {
            node.value = this.getVal(value, vm);
        })
        node.value = this.getVal(value, vm);

        //视图-》数据-》视图
        node.addEventListener('input',(e)=>{
            this.setVal(value,vm,e.target.value)
        })
    },
    //事件绑定
    on(node, value, vm, eventName) {
        let func = vm.$methods && vm.$methods[value]
        // 绑定事件   this指向vue实例
        node.addEventListener(eventName, func.bind(vm), false)
    },
    //属性绑定
    bind(node, value, vm, eventName) {
        let content = this.getVal(value, vm);
        node.setAttribute(eventName, content);
        new Watcher(value, vm, () => {
            let content = this.getVal(value, vm);
            node.setAttribute(eventName, content);
        })
    },
}
//vue的实例对象
class Vue {
    constructor(opt) {
        this.$el = opt.el;
        this.$data = opt.data;
        this.$methods = opt.methods;
        this.$vm = opt;
        if (this.$el) {
            //1.数据监听
            new Observer(this.$data)
            //2.数据解析
            new Compile(this.$el, this);
        }
        this.proxy(this.$data);
    }
    //代理
    proxy(data){
       Object.keys(data).forEach(item=>{
        Object.defineProperty(this,item,{
            get(){
                return data[item]
            },
            set(newVal){
                data[item]=newVal;
            }
        })
       })
    }
}